#pragma once
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "Task.hpp"
#include <vector>
#include <queue>
#include <string>
#include <mutex>

static int gNum = 5;

template <class T>
class ThreadPool; // 声明一下

// 线程池的内部this，当成结构体使用
template <class T>
class ThreadData
{
public:
    ThreadData(ThreadPool<T> *threadpool, const std::string &name)
        : _threadpool(threadpool), name_(name)
    {
    }

public:
    ThreadPool<T> *_threadpool;
    std::string name_; // 线程名字
};

template <class T>
class ThreadPool
{
private:
    static void *handTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
        while (true)
        {
            T t;
            //  进行加锁
            //  td->_threadpool->threadlock();
            //  这里还拿不到锁
            {
                LockGuard lock(td->_threadpool->mutex());
                while (td->_threadpool->queueIsempty())
                {
                    // 此时队列为空,将线程挂起
                    td->_threadpool->threadwait();
                }
                // 此时要拿取出任务，本质是从公共队列中的任务，拿到自己独立的栈结构中
                t = td->_threadpool->pop();
            }
            // 处理的结果，可以等到解锁之后完成，提高效率
            // std::cout << td->name_ << " 获取了一个任务 " << t.toTaskName() << " 并处理完成,处理的结果为: " << t() << std::endl;
            t();
        }
        delete td;
        return nullptr;
    }

    // 单例模式需要将构造函数私有，禁止外部生成对象
    ThreadPool(const int &num = gNum) : _num(num)
    {
        // 初始化锁以及条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }

public:
    void threadlock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void threadunlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    bool queueIsempty()
    {
        return _task_q.empty();
    }

    void threadwait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    T pop()
    {
        T t = _task_q.front();
        _task_q.pop();
        return t;
    }

    pthread_mutex_t *mutex()
    {
        return &_mutex;
    }

public:
    // 给外部提供一个线程开始工作的方法
    void Thread_Work()
    {
        for (const auto &t : _threads)
        {
            // 当传递的参数使用
            ThreadData<T> *td = new ThreadData<T>(this, t->threadName());
            t->start(handTask, td); // 线程从队列中抢夺任务，开始执行
            std::cout << t->threadName() << " start work ..." << std::endl;
        }
    }

    // 提供给外面插入任务的接口
    void Push(const T &in)
    {
        LockGuard lock(&_mutex);
        _task_q.push(in); // 把任务Push进入队列中
        // 此时保证队列中一定有一个数据,唤醒条件变量下的线程
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);

        // 释放线程池中的线程
        for (const auto &t : _threads)
        {
            delete t;
        }
    }

    // 有this指针
    static ThreadPool<T> *getInstance()
    {
        // 这个代表该线程已经单例了
        if (nullptr == _tp)
        {
            // 在多线程都要生成单例的时候，需要加锁
            _singleton_lock.lock();
            if (_tp == nullptr)
            {
                // 此时没有创建对象
                _tp = new ThreadPool<T>();
            }
            _singleton_lock.unlock();
        }
        return _tp;
    }

private:
    int _num;                       // 多少个线程
    std::vector<Thread *> _threads; // 使用vector来保存线程池中的线程
    std::queue<T> _task_q;          // 队列保存任务
    pthread_mutex_t _mutex;         // 队列中插入数据中保证原子性
    pthread_cond_t _cond;           // 条件变量

    static ThreadPool<T> *_tp;         // 懒汉模式的单例
    static std::mutex _singleton_lock; // 单例锁
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

template <class T>
std::mutex ThreadPool<T>::_singleton_lock;